<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>谈谈你对生成器以及协程的理解。 | yanyan</title>
    <meta name="description" content="Yan&#39;s blog">
    <meta name="generator" content="VuePress 1.4.0">
    <script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.slim.min.js"></script>
  <script src="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.css">
  <link rel="shortcut icon" type="image/x-icon" href="./favicon.ico">
    
    <link rel="preload" href="/assets/css/0.styles.e1b3f17d.css" as="style"><link rel="preload" href="/assets/js/app.e358a08d.js" as="script"><link rel="preload" href="/assets/js/2.88fa18d1.js" as="script"><link rel="preload" href="/assets/js/44.84cd8367.js" as="script"><link rel="prefetch" href="/assets/js/10.23baf844.js"><link rel="prefetch" href="/assets/js/11.45c148ba.js"><link rel="prefetch" href="/assets/js/12.e5930132.js"><link rel="prefetch" href="/assets/js/13.0547cd14.js"><link rel="prefetch" href="/assets/js/14.3e67795b.js"><link rel="prefetch" href="/assets/js/15.51129890.js"><link rel="prefetch" href="/assets/js/16.6987f89d.js"><link rel="prefetch" href="/assets/js/17.2807cff5.js"><link rel="prefetch" href="/assets/js/18.855e1707.js"><link rel="prefetch" href="/assets/js/19.6da24791.js"><link rel="prefetch" href="/assets/js/20.e24d4aef.js"><link rel="prefetch" href="/assets/js/21.6efc6fba.js"><link rel="prefetch" href="/assets/js/22.10447f0f.js"><link rel="prefetch" href="/assets/js/23.9154cc24.js"><link rel="prefetch" href="/assets/js/24.9ad529fc.js"><link rel="prefetch" href="/assets/js/25.4c092e0a.js"><link rel="prefetch" href="/assets/js/26.debdaa01.js"><link rel="prefetch" href="/assets/js/27.8b90b660.js"><link rel="prefetch" href="/assets/js/28.1a323e01.js"><link rel="prefetch" href="/assets/js/29.6f108fc9.js"><link rel="prefetch" href="/assets/js/3.7210d3aa.js"><link rel="prefetch" href="/assets/js/30.e7df1937.js"><link rel="prefetch" href="/assets/js/31.2cb3120f.js"><link rel="prefetch" href="/assets/js/32.eb64932c.js"><link rel="prefetch" href="/assets/js/33.cac3e2f0.js"><link rel="prefetch" href="/assets/js/34.19ea35c4.js"><link rel="prefetch" href="/assets/js/35.fadf5d03.js"><link rel="prefetch" href="/assets/js/36.88b681f1.js"><link rel="prefetch" href="/assets/js/37.2a799db9.js"><link rel="prefetch" href="/assets/js/38.2741a2bf.js"><link rel="prefetch" href="/assets/js/39.359ceb72.js"><link rel="prefetch" href="/assets/js/4.9e938666.js"><link rel="prefetch" href="/assets/js/40.56fd4a10.js"><link rel="prefetch" href="/assets/js/41.e72117ad.js"><link rel="prefetch" href="/assets/js/42.63a6e190.js"><link rel="prefetch" href="/assets/js/43.c8072421.js"><link rel="prefetch" href="/assets/js/45.0ac810b0.js"><link rel="prefetch" href="/assets/js/46.bb83ff34.js"><link rel="prefetch" href="/assets/js/47.a9333a81.js"><link rel="prefetch" href="/assets/js/48.526b5494.js"><link rel="prefetch" href="/assets/js/49.73b61cc6.js"><link rel="prefetch" href="/assets/js/5.88b252c7.js"><link rel="prefetch" href="/assets/js/50.f34ab799.js"><link rel="prefetch" href="/assets/js/51.d06a49d9.js"><link rel="prefetch" href="/assets/js/52.348d5482.js"><link rel="prefetch" href="/assets/js/6.0face56b.js"><link rel="prefetch" href="/assets/js/7.31eca58d.js"><link rel="prefetch" href="/assets/js/8.69e9ce95.js"><link rel="prefetch" href="/assets/js/9.f25df9e1.js">
    <link rel="stylesheet" href="/assets/css/0.styles.e1b3f17d.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">yanyan</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <!----></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><!---->  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><a href="/html/001" class="sidebar-heading clickable"><span>HTML</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/css/image" class="sidebar-heading clickable"><span>CSS</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js/001" class="sidebar-heading clickable"><span>JS-基础</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-v8/001" class="sidebar-heading clickable"><span>JS-V8引擎原理 </span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-async/001" class="sidebar-heading clickable open"><span>JS-异步I/O及异步编程</span> <span class="arrow down"></span></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/js-async/001.html" class="sidebar-link">01.nodejs中的异步、非阻塞I/O是如何实现的？</a></li><li><a href="/js-async/002.html" class="sidebar-link">02.JS异步编程有哪些方案？为什么会出现这些方案？</a></li><li><a href="/js-async/003.html" class="sidebar-link">03.能不能简单实现一下 node 中回调函数的机制？</a></li><li><a href="/js-async/004.html" class="sidebar-link">04.Promise之问(一)——Promise 凭借什么消灭了回调地狱？</a></li><li><a href="/js-async/005.html" class="sidebar-link">05.Promise之问(一)——Promise之问(二)——为什么Promise要引入微任务？</a></li><li><a href="/js-async/006.html" class="sidebar-link">06.Promise之问(三)——Promise 如何实现链式调用？</a></li><li><a href="/js-async/007.html" class="sidebar-link">07.Promise之问(四)——实现Promise的 resolve、reject 和 finally</a></li><li><a href="/js-async/008.html" class="sidebar-link">08.Promise 之问(五)——实现Promise的 all 和 race</a></li><li><a href="/js-async/009.html" class="active sidebar-link">09.谈谈你对生成器以及协程的理解。</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/js-async/009.html#生成器执行流程" class="sidebar-link">生成器执行流程</a></li><li class="sidebar-sub-header"><a href="/js-async/009.html#yield" class="sidebar-link">yield*</a></li><li class="sidebar-sub-header"><a href="/js-async/009.html#生成器实现机制-协程" class="sidebar-link">生成器实现机制——协程</a></li></ul></li><li><a href="/js-async/010.html" class="sidebar-link">10.如何让 Generator 的异步代码按顺序执行完毕？</a></li><li><a href="/js-async/011.html" class="sidebar-link">11.解释一下async/await的运行机制。？</a></li><li><a href="/js-async/012.html" class="sidebar-link">12.forEach 中用 await 会产生什么问题?怎么解决这个问题？</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/http/000" class="sidebar-heading clickable"><span>HTTP</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/extend/002" class="sidebar-heading clickable"><span>拓展阅读</span> <span class="arrow right"></span></a> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="谈谈你对生成器以及协程的理解。"><a href="#谈谈你对生成器以及协程的理解。" class="header-anchor">#</a> 谈谈你对生成器以及协程的理解。</h1> <p>生成器(Generator)是 ES6 中的新语法，相对于之前的异步语法，上手的难度还是比较大的。因此这里我们先来好好熟悉一下 Generator 语法。</p> <h2 id="生成器执行流程"><a href="#生成器执行流程" class="header-anchor">#</a> 生成器执行流程</h2> <p>上面是生成器函数？</p> <p>生成器是一个带星号的&quot;函数&quot;(注意：它并不是真正的函数)，可以通过yield关键字暂停执行和恢复执行的</p> <p>举个例子:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">gen</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'enter'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token keyword">yield</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token keyword">yield</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token number">3</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">var</span> g <span class="token operator">=</span> <span class="token function">gen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 阻塞住，不会执行任何语句</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> g<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// object  看到了吗？不是&quot;function&quot;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>g<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>g<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>g<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>g<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 输出结果</span>
<span class="token comment">// object</span>
<span class="token comment">// enter</span>
<span class="token comment">// Object {value: 1, done: false}</span>
<span class="token comment">// Object {value: 2, done: false}</span>
<span class="token comment">// Object { value: undefined, done: true }</span>

</code></pre></div><p>由此可以看到，生成器的执行有这样几个关键点:</p> <ol><li>调用 gen() 后，程序会阻塞住，不会执行任何语句。</li> <li>调用 g.next() 后，程序继续执行，直到遇到 yield 程序暂停。</li> <li>next 方法返回一个对象， 有两个属性: value 和 done。value 为当前 yield 后面的结果，done 表示是否执行完，遇到了return 后，done 会由false变为true。</li></ol> <h2 id="yield"><a href="#yield" class="header-anchor">#</a> yield*</h2> <p>当一个生成器要调用另一个生成器时，使用 yield* 就变得十分方便。比如下面的例子:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">gen1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">yield</span> <span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token keyword">yield</span> <span class="token number">4</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">gen2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">yield</span> <span class="token number">2</span><span class="token punctuation">;</span>
    <span class="token keyword">yield</span> <span class="token number">3</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>我们想要按照1234的顺序执行，如何来做呢？</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">gen1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">yield</span> <span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token keyword">yield</span><span class="token operator">*</span> <span class="token function">gen2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">yield</span> <span class="token number">4</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>这样修改之后，之后依次调用next即可</p> <h2 id="生成器实现机制-协程"><a href="#生成器实现机制-协程" class="header-anchor">#</a> 生成器实现机制——协程</h2> <p>可能你会比较好奇，生成器究竟是如何让函数暂停, 又会如何恢复的呢？接下来我们就来对其中的执行机制——<code>协程</code>一探究竟</p> <h3 id="什么是协程？"><a href="#什么是协程？" class="header-anchor">#</a> 什么是协程？</h3> <p>协程是一种比线程更加轻量级的存在，协程处在线程的环境中，一个线程可以存在多个协程，可以将协程理解为线程中的一个个任务。不像进程和线程，协程并不受操作系统的管理，而是被具体的应用程序代码所控制。</p> <h3 id="协程的运作过程"><a href="#协程的运作过程" class="header-anchor">#</a> 协程的运作过程</h3> <p>那你可能要问了，JS 不是单线程执行的吗，开这么多协程难道可以一起执行吗？</p> <p>答案是：并不能。一个线程一次只能执行一个协程。比如当前执行 A 协程，另外还有一个 B 协程，如果想要执行 B 的任务，就必须在 A 协程中将JS 线程的控制权转交给 B协程，那么现在 B 执行，A 就相当于处于暂停的状态。</p> <p>举个具体的例子:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span><span class="token operator">*</span> <span class="token constant">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;我是A&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">yield</span> <span class="token constant">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// A停住，在这里转交线程执行权给B</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;结束了&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token constant">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;我是B&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token number">100</span><span class="token punctuation">;</span><span class="token comment">// 返回，并且将线程执行权还给A</span>
<span class="token punctuation">}</span>
<span class="token keyword">let</span> gen <span class="token operator">=</span> <span class="token constant">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
gen<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 我是A</span>
<span class="token comment">// 我是B</span>
<span class="token comment">// 结束了</span>
</code></pre></div><p>在这个过程中，A 将执行权交给 B，也就是 A 启动 B，我们也称 A 是 B 的父协程。因此 B 当中最后return 100其实是将 100 传给了父协程。</p> <p>需要强调的是，对于协程来说，它并不受操作系统的控制，完全由用户自定义切换，因此并没有进程/线程上下文切换的开销，这是高性能的重要原因。</p> <p>OK, 原理就说到这里。可能你还会有疑问: 这个生成器不就暂停-恢复、暂停-恢复这样执行的吗？它和异步有什么关系？而且，每次执行都要调用next，能不能让它一次性执行完毕呢？下一节我们就来仔细拆解这些问题。</p></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/js-async/008.html" class="prev">
        08.Promise 之问(五)——实现Promise的 all 和 race
      </a></span> <span class="next"><a href="/js-async/010.html">
        10.如何让 Generator 的异步代码按顺序执行完毕？
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/assets/js/app.e358a08d.js" defer></script><script src="/assets/js/2.88fa18d1.js" defer></script><script src="/assets/js/44.84cd8367.js" defer></script>
  </body>
</html>
